#!/usr/bin/env python3
"""
依赖项管理引擎
用于处理Python包的安装、卸载、更新等操作
"""

import subprocess
import sys
import os
import json
import logging
import pkg_resources
import time
from datetime import datetime
from typing import Dict, List, Tuple, Optional

# 在文件顶部添加核心包保护列表
PROTECTED_PACKAGES = [
    'alembic',
    'apscheduler',
    'bcrypt',
    'blinker',
    'certifi',
    'charset-normalizer',
    'click',
    'cryptography',
    'flask',
    'flask-cors',
    'flask-login',
    'flask-migrate',
    'flask-sqlalchemy',
    'flask-wtf',
    'greenlet',
    'idna',
    'itsdangerous',
    'jinja2',
    'mako',
    'markupsafe',
    'pip',
    'pkg-resources',
    'pycryptodome',
    'python-dateutil',
    'python-dotenv',
    'pytz',
    'requests',
    'setuptools',
    'six',
    'sqlalchemy',
    'typing-extensions',
    'urllib3',
    'werkzeug',
    'wheel',
]

# 核心包描述
CORE_PACKAGE_DESCRIPTIONS = {
    'flask': 'Web框架核心',
    'flask-sqlalchemy': '数据库ORM',
    'flask-migrate': '数据库迁移',
    'flask-login': '用户认证',
    'flask-wtf': '表单处理',
    'flask-cors': '跨域支持',
    'sqlalchemy': '数据库ORM核心',
    'alembic': '数据库迁移工具',
    'requests': 'HTTP请求库',
    'urllib3': 'HTTP客户端',
    'certifi': 'SSL证书验证',
    'charset-normalizer': '字符编码处理',
    'idna': '国际化域名处理',
    'pkg-resources': '包资源管理',
    'setuptools': '包安装工具',
    'pip': '包管理器',
    'wheel': '包构建工具',
    'python-dateutil': '日期时间处理',
    'pytz': '时区处理',
    'six': 'Python 2/3兼容性',
    'typing-extensions': '类型提示扩展',
    'markupsafe': '安全标记',
    'jinja2': '模板引擎',
    'werkzeug': 'WSGI工具库',
    'click': '命令行工具',
    'itsdangerous': '安全签名',
    'blinker': '信号处理',
    'greenlet': '协程支持',
    'mako': '模板引擎',
    'apscheduler': '任务调度器',
    'python-dotenv': '环境变量管理',
    'cryptography': '加密库',
    'bcrypt': '密码哈希',
    'pycryptodome': '加密库',
}

logger = logging.getLogger('PackageManager')

class PackageManager:
    """Python包管理器"""
    
    def __init__(self, mirror_source=None, progress_callback=None):
        self.pip_path = sys.executable.replace('python', 'pip')
        if not os.path.exists(self.pip_path):
            # 尝试其他可能的pip路径
            possible_paths = [
                sys.executable.replace('python', 'pip3'),
                'pip',
                'pip3'
            ]
            for path in possible_paths:
                if os.path.exists(path) or self._check_command_exists(path):
                    self.pip_path = path
                    break
        
        # 镜像源配置
        self.mirror_source = mirror_source or "https://mirrors.aliyun.com/pypi/simple/"
        self.available_mirrors = {
            "阿里云": "https://mirrors.aliyun.com/pypi/simple/",
            "清华大学": "https://pypi.tuna.tsinghua.edu.cn/simple/",
            "中国科技大学": "https://pypi.mirrors.ustc.edu.cn/simple/",
            "豆瓣": "https://pypi.douban.com/simple/",
            "华为云": "https://repo.huaweicloud.com/repository/pypi/simple/",
            "腾讯云": "https://mirrors.cloud.tencent.com/pypi/simple/",
            "官方源": "https://pypi.org/simple/"
        }
        
        # 进度回调函数
        self.progress_callback = progress_callback
    
    def _log_progress(self, message: str, level: str = "info"):
        """记录进度信息"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        log_message = f"[{timestamp}] {message}"
        
        if level == "info":
            logger.info(log_message)
        elif level == "warning":
            logger.warning(log_message)
        elif level == "error":
            logger.error(log_message)
        
        # 调用进度回调函数
        if self.progress_callback:
            try:
                self.progress_callback(log_message, level)
            except:
                pass
    
    def _check_command_exists(self, command: str) -> bool:
        """检查命令是否存在"""
        try:
            result = subprocess.run([command, '--version'], 
                                  capture_output=True, text=True, timeout=5)
            return result.returncode == 0
        except:
            return False
    
    def get_installed_packages(self) -> List[Dict]:
        """获取已安装的包列表"""
        try:
            self._log_progress("正在获取已安装包列表...")
            installed_packages = []
            
            # 使用pip list命令获取更准确的包列表
            try:
                result = subprocess.run(
                    [self.pip_path, 'list', '--format=json'],
                    capture_output=True,
                    text=True,
                    timeout=30
                )
                
                if result.returncode == 0:
                    import json
                    pip_packages = json.loads(result.stdout)
                    
                    for package in pip_packages:
                        package_info = {
                            'name': package['name'],
                            'version': package['version'],
                            'location': self._get_package_location(package['name']),
                            'summary': self._get_package_summary(package['name']),
                            'requires': self._get_package_requires(package['name'])
                        }
                        installed_packages.append(package_info)
                else:
                    # 如果pip list失败，回退到pkg_resources
                    self._log_progress("pip list失败，使用pkg_resources获取包列表", "warning")
                    for dist in pkg_resources.working_set:
                        package_info = {
                            'name': dist.project_name,
                            'version': dist.version,
                            'location': dist.location,
                            'summary': self._get_package_summary(dist.project_name),
                            'requires': [str(req) for req in dist.requires()] if dist.requires() else []
                        }
                        installed_packages.append(package_info)
                        
            except Exception as e:
                self._log_progress(f"获取包列表时出错: {str(e)}", "warning")
                # 回退到pkg_resources
                for dist in pkg_resources.working_set:
                    package_info = {
                        'name': dist.project_name,
                        'version': dist.version,
                        'location': dist.location,
                        'summary': self._get_package_summary(dist.project_name),
                        'requires': [str(req) for req in dist.requires()] if dist.requires() else []
                    }
                    installed_packages.append(package_info)
            
            # 按名称排序
            installed_packages.sort(key=lambda x: x['name'].lower())
            self._log_progress(f"已获取 {len(installed_packages)} 个已安装包")
            return installed_packages
            
        except Exception as e:
            self._log_progress(f"获取已安装包列表失败: {str(e)}", "error")
            return []
    
    def _get_package_summary(self, package_name: str) -> str:
        """获取包的描述信息"""
        try:
            # 尝试获取包的元数据
            dist = pkg_resources.get_distribution(package_name)
            if hasattr(dist, 'get_metadata'):
                try:
                    metadata = dist.get_metadata('METADATA')
                    for line in metadata.split('\n'):
                        if line.startswith('Summary:'):
                            return line[8:].strip()
                except:
                    pass
        except:
            pass
        return ""
    
    def _get_package_location(self, package_name: str) -> str:
        """获取包的安装位置"""
        try:
            dist = pkg_resources.get_distribution(package_name)
            return dist.location
        except:
            return ""
    
    def _get_package_requires(self, package_name: str) -> List[str]:
        """获取包的依赖列表"""
        try:
            dist = pkg_resources.get_distribution(package_name)
            return [str(req) for req in dist.requires()] if dist.requires() else []
        except:
            return []
    
    def install_package(self, package_name: str, version: str = None) -> Tuple[bool, str, str]:
        """
        安装包
        :param package_name: 包名
        :param version: 版本号（可选）
        :return: (是否成功, 输出信息, 错误信息)
        """
        # 检查是否为受保护的核心包
        if self.is_protected_package(package_name):
            error_msg = f"不允许安装/覆盖核心包: {package_name}。这是系统运行必需的核心包，安装可能导致系统不稳定。"
            self._log_progress(error_msg, "warning")
            return False, "", error_msg
        
        try:
            start_time = datetime.now()
            
            # 安全处理版本参数
            if version is None or version.strip() == '':
                version = None
                package_spec = package_name
            else:
                package_spec = f"{package_name}=={version}"
            
            self._log_progress(f"开始安装包: {package_spec}")
            self._log_progress(f"使用镜像源: {self.mirror_source}")
            
            # 构建安装命令，优先使用预编译包
            if version:
                install_cmd = [self.pip_path, 'install', f'{package_name}=={version}', '--no-cache-dir', '--prefer-binary', '-i', self.mirror_source, '--verbose']
            else:
                install_cmd = [self.pip_path, 'install', package_name, '--no-cache-dir', '--prefer-binary', '-i', self.mirror_source, '--verbose']
            
            # 执行安装，最多重试3次
            max_retries = 3
            for attempt in range(max_retries):
                try:
                    self._log_progress(f"安装尝试 {attempt + 1}/{max_retries}...")
                    
                    # 使用实时输出
                    process = subprocess.Popen(
                        install_cmd,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.STDOUT,
                        text=True,
                        bufsize=1,
                        universal_newlines=True
                    )
                    
                    output_lines = []
                    start_time_process = time.time()
                    
                    # 实时读取输出
                    while True:
                        if process.stdout is None:
                            break
                        line = process.stdout.readline()
                        if not line and process.poll() is not None:
                            break
                        
                        if line:
                            line = line.strip()
                            output_lines.append(line)
                            
                            # 记录更详细的进度信息
                            if any(keyword in line.lower() for keyword in ['collecting', 'downloading', 'installing', 'building', 'compiling', 'processing', 'unpacking', 'running', 'creating', 'writing', 'copying', 'linking']):
                                self._log_progress(f"进度: {line}")
                            elif any(keyword in line.lower() for keyword in ['warning', 'error', 'failed', 'exception', 'traceback']):
                                self._log_progress(f"警告/错误: {line}", "warning" if "warning" in line.lower() else "error")
                            elif line.strip() and not line.startswith('DEPRECATION') and not line.startswith('WARNING: pip'):
                                # 记录所有有意义的非空行，但过滤掉一些无用的警告
                                self._log_progress(f"信息: {line}")
                            
                            # 检查是否超时
                            if time.time() - start_time_process > 300:  # 5分钟超时
                                process.terminate()
                                self._log_progress("安装超时，正在终止进程...", "warning")
                                break
                    
                    # 等待进程结束
                    return_code = process.wait()
                    
                    if return_code == 0:
                        end_time = datetime.now()
                        duration = (end_time - start_time).total_seconds()
                        self._log_progress(f"包安装成功: {package_spec} (耗时: {duration:.1f}秒)")
                        return True, '\n'.join(output_lines), ""
                    else:
                        error_msg = f"包安装失败 (尝试 {attempt + 1}/{max_retries}): {package_spec}"
                        self._log_progress(error_msg, "warning")
                        
                        # 分析错误原因
                        error_output = '\n'.join(output_lines)
                        if "No matching distribution found" in error_output:
                            self._log_progress(f"错误分析: 未找到匹配的包版本 {package_spec}", "error")
                        elif "Could not find a version" in error_output:
                            self._log_progress(f"错误分析: 无法找到指定版本 {package_spec}", "error")
                        elif "Failed building wheel" in error_output:
                            self._log_progress(f"错误分析: 编译失败，建议使用预编译版本", "error")
                        elif "Permission denied" in error_output:
                            self._log_progress(f"错误分析: 权限不足，可能需要管理员权限", "error")
                        elif "Network" in error_output or "timeout" in error_output.lower():
                            self._log_progress(f"错误分析: 网络连接问题，请检查网络或更换镜像源", "error")
                        
                        if attempt < max_retries - 1:
                            self._log_progress("等待10秒后重试...")
                            time.sleep(10)
                        else:
                            self._log_progress(f"包安装最终失败: {package_spec}", "error")
                            return False, '\n'.join(output_lines), error_msg
                            
                except subprocess.TimeoutExpired:
                    error_msg = f"安装超时 (尝试 {attempt + 1}/{max_retries}): {package_spec}"
                    self._log_progress(error_msg, "error")
                    if attempt < max_retries - 1:
                        self._log_progress("等待10秒后重试...")
                        time.sleep(10)
                    else:
                        return False, "", error_msg
                except Exception as e:
                    error_msg = f"安装过程异常 (尝试 {attempt + 1}/{max_retries}): {str(e)}"
                    self._log_progress(error_msg, "error")
                    if attempt < max_retries - 1:
                        self._log_progress("等待10秒后重试...")
                        time.sleep(10)
                    else:
                        return False, "", error_msg
                        
        except Exception as e:
            error_msg = f"包安装异常: {str(e)}"
            self._log_progress(error_msg, "error")
            return False, "", error_msg
    
    def uninstall_package(self, package_name: str) -> Tuple[bool, str, str]:
        """
        卸载包
        :param package_name: 包名
        :return: (是否成功, 输出信息, 错误信息)
        """
        # 检查是否为受保护的核心包
        if self.is_protected_package(package_name):
            error_msg = f"无法卸载核心包: {package_name}。这是系统运行必需的核心包，卸载可能导致系统无法正常工作。"
            self._log_progress(error_msg, "error")
            return False, "", error_msg
        
        try:
            start_time = datetime.now()
            
            self._log_progress(f"开始卸载包: {package_name}")
            
            uninstall_cmd = [self.pip_path, 'uninstall', '-y', package_name]
            
            # 执行卸载
            process = subprocess.Popen(
                uninstall_cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                universal_newlines=True
            )
            
            output_lines = []
            start_time_process = time.time()
            
            # 实时读取输出
            while True:
                if process.stdout is None:
                    break
                line = process.stdout.readline()
                if not line and process.poll() is not None:
                    break
                
                if line:
                    line = line.strip()
                    output_lines.append(line)
                    
                    # 记录更详细的进度信息
                    if any(keyword in line.lower() for keyword in ['collecting', 'downloading', 'installing', 'building', 'compiling', 'processing', 'unpacking', 'running', 'creating', 'writing', 'copying', 'linking']):
                        self._log_progress(f"进度: {line}")
                    elif any(keyword in line.lower() for keyword in ['warning', 'error', 'failed', 'exception', 'traceback']):
                        self._log_progress(f"警告/错误: {line}", "warning" if "warning" in line.lower() else "error")
                    elif line.strip() and not line.startswith('DEPRECATION') and not line.startswith('WARNING: pip'):
                        # 记录所有有意义的非空行，但过滤掉一些无用的警告
                        self._log_progress(f"信息: {line}")
                    
                    # 检查是否超时
                    if time.time() - start_time_process > 60:  # 1分钟超时
                        process.terminate()
                        self._log_progress("卸载超时，正在终止进程...", "warning")
                        break
            
            # 等待进程结束
            return_code = process.wait()
            
            end_time = datetime.now()
            duration = (end_time - start_time).total_seconds()
            
            if return_code == 0:
                self._log_progress(f"包卸载成功: {package_name} (耗时: {duration:.1f}秒)")
                return True, '\n'.join(output_lines), ""
            else:
                error_msg = f"包卸载失败: {package_name}"
                self._log_progress(error_msg, "error")
                return False, '\n'.join(output_lines), error_msg
                
        except subprocess.TimeoutExpired:
            error_msg = f"包卸载超时: {package_name}"
            self._log_progress(error_msg, "error")
            return False, "", error_msg
        except Exception as e:
            error_msg = f"包卸载异常: {str(e)}"
            self._log_progress(error_msg, "error")
            return False, "", error_msg
    
    def upgrade_package(self, package_name: str) -> Tuple[bool, str, str]:
        """
        升级包
        :param package_name: 包名
        :return: (是否成功, 输出信息, 错误信息)
        """
        # 检查是否为受保护的核心包
        if self.is_protected_package(package_name):
            error_msg = f"不建议升级核心包: {package_name}。这是系统运行必需的核心包，升级可能导致系统不稳定。如需升级，请手动操作。"
            self._log_progress(error_msg, "warning")
            return False, "", error_msg
        
        try:
            start_time = datetime.now()
            
            self._log_progress(f"开始升级包: {package_name}")
            self._log_progress(f"使用镜像源: {self.mirror_source}")
            
            upgrade_cmd = [self.pip_path, 'install', '--upgrade', package_name, '--no-cache-dir', '--prefer-binary', '-i', self.mirror_source, '--verbose']
            
            # 执行升级，最多重试3次
            max_retries = 3
            for attempt in range(max_retries):
                try:
                    self._log_progress(f"升级尝试 {attempt + 1}/{max_retries}...")
                    
                    # 使用实时输出
                    process = subprocess.Popen(
                        upgrade_cmd,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.STDOUT,
                        text=True,
                        bufsize=1,
                        universal_newlines=True
                    )
                    
                    output_lines = []
                    start_time_process = time.time()
                    
                    # 实时读取输出
                    while True:
                        if process.stdout is None:
                            break
                        line = process.stdout.readline()
                        if not line and process.poll() is not None:
                            break
                        
                        if line:
                            line = line.strip()
                            output_lines.append(line)
                            
                            # 记录更详细的进度信息
                            if any(keyword in line.lower() for keyword in ['collecting', 'downloading', 'installing', 'building', 'compiling', 'processing', 'unpacking', 'running', 'creating', 'writing', 'copying', 'linking']):
                                self._log_progress(f"进度: {line}")
                            elif any(keyword in line.lower() for keyword in ['warning', 'error', 'failed', 'exception', 'traceback']):
                                self._log_progress(f"警告/错误: {line}", "warning" if "warning" in line.lower() else "error")
                            elif line.strip() and not line.startswith('DEPRECATION') and not line.startswith('WARNING: pip'):
                                # 记录所有有意义的非空行，但过滤掉一些无用的警告
                                self._log_progress(f"信息: {line}")
                            
                            # 检查是否超时
                            if time.time() - start_time_process > 300:  # 5分钟超时
                                process.terminate()
                                self._log_progress("升级超时，正在终止进程...", "warning")
                                break
                    
                    # 等待进程结束
                    return_code = process.wait()
                    
                    # 清理进程对象
                    if background_manager is not None:
                        background_manager.process = None
                    
                    if return_code == 0:
                        end_time = datetime.now()
                        duration = (end_time - start_time).total_seconds()
                        self._log_progress(f"包升级成功: {package_name} (耗时: {duration:.1f}秒)")
                        return True, '\n'.join(output_lines), ""
                    else:
                        error_msg = f"包升级失败 (尝试 {attempt + 1}/{max_retries}): {package_name}"
                        self._log_progress(error_msg, "warning")
                        
                        if attempt < max_retries - 1:
                            self._log_progress("等待10秒后重试...")
                            time.sleep(10)
                        else:
                            self._log_progress(f"包升级最终失败: {package_name}", "error")
                            return False, '\n'.join(output_lines), error_msg
                            
                except Exception as e:
                    error_msg = f"升级过程异常 (尝试 {attempt + 1}/{max_retries}): {str(e)}"
                    self._log_progress(error_msg, "error")
                    if attempt < max_retries - 1:
                        self._log_progress("等待10秒后重试...")
                        time.sleep(10)
                    else:
                        return False, "", error_msg
                        
        except Exception as e:
            error_msg = f"包升级异常: {str(e)}"
            self._log_progress(error_msg, "error")
            return False, "", error_msg
    
    def search_package(self, package_name: str) -> List[Dict]:
        """
        搜索包信息
        :param package_name: 包名
        :return: 包信息列表
        """
        try:
            search_cmd = [self.pip_path, 'search', package_name]
            
            # 注意：pip search 命令在较新版本中已被禁用
            # 这里我们使用PyPI API来搜索
            import requests
            
            url = f"https://pypi.org/pypi/{package_name}/json"
            response = requests.get(url, timeout=10)
            
            if response.status_code == 200:
                data = response.json()
                return [{
                    'name': data['info']['name'],
                    'version': data['info']['version'],
                    'summary': data['info']['summary'],
                    'description': data['info']['description'],
                    'home_page': data['info']['home_page'],
                    'author': data['info']['author'],
                    'license': data['info']['license']
                }]
            else:
                return []
                
        except Exception as e:
            logger.error(f"搜索包失败: {str(e)}")
            return []
    
    def get_package_info(self, package_name: str) -> Optional[Dict]:
        """
        获取包的详细信息
        :param package_name: 包名
        :return: 包信息
        """
        try:
            dist = pkg_resources.get_distribution(package_name)
            return {
                'name': dist.project_name,
                'version': dist.version,
                'location': dist.location,
                'summary': self._get_package_summary(package_name),
                'requires': [str(req) for req in dist.requires()] if dist.requires() else []
            }
        except:
            return None
    
    def check_package_installed(self, package_name: str) -> bool:
        """
        检查包是否已安装
        :param package_name: 包名
        :return: 是否已安装
        """
        try:
            # 使用pip list命令检查包是否已安装，这样更准确
            result = subprocess.run(
                [self.pip_path, 'list', '--format=freeze'],
                capture_output=True,
                text=True,
                timeout=30
            )
            
            if result.returncode == 0:
                installed_packages = result.stdout.strip().split('\n')
                for package_line in installed_packages:
                    if package_line.startswith(f"{package_name}=="):
                        return True
                return False
            else:
                # 如果pip list失败，回退到pkg_resources
                pkg_resources.get_distribution(package_name)
                return True
        except:
            return False
    
    def is_protected_package(self, package_name: str) -> bool:
        """
        检查包是否为受保护的核心包
        :param package_name: 包名
        :return: 是否为受保护包
        """
        return package_name.lower() in [pkg.lower() for pkg in PROTECTED_PACKAGES]
    
    def get_protected_packages(self) -> List[str]:
        """
        获取受保护的核心包列表
        :return: 受保护包列表
        """
        return PROTECTED_PACKAGES.copy()
    
    def get_core_package_description(self, package_name: str) -> str:
        """
        获取核心包描述
        :param package_name: 包名
        :return: 包描述
        """
        return CORE_PACKAGE_DESCRIPTIONS.get(package_name.lower(), '核心系统包')
    
    def get_available_mirrors(self) -> Dict[str, str]:
        """
        获取可用的镜像源列表
        :return: 镜像源字典
        """
        return self.available_mirrors
    
    def set_mirror_source(self, mirror_name: str) -> bool:
        """
        设置镜像源
        :param mirror_name: 镜像源名称
        :return: 是否设置成功
        """
        if mirror_name in self.available_mirrors:
            self.mirror_source = self.available_mirrors[mirror_name]
            logger.info(f"镜像源已设置为: {mirror_name} ({self.mirror_source})")
            return True
        else:
            logger.error(f"未知的镜像源: {mirror_name}")
            return False
    
    def get_current_mirror(self) -> str:
        """
        获取当前镜像源
        :return: 当前镜像源URL
        """
        return self.mirror_source
    
    def get_package_install_advice(self, package_name: str) -> str:
        """
        获取包安装建议
        :param package_name: 包名
        :return: 安装建议
        """
        # 已知需要编译的包
        compile_heavy_packages = {
            'pandas': '建议使用预编译版本，如 pandas==1.5.3',
            'numpy': '建议使用预编译版本，如 numpy==1.24.3',
            'scipy': '建议使用预编译版本，如 scipy==1.10.1',
            'matplotlib': '建议使用预编译版本，如 matplotlib==3.7.1',
            'pillow': '建议使用预编译版本，如 pillow==9.5.0',
            'cryptography': '建议使用预编译版本，如 cryptography==41.0.1',
            'psycopg2': '建议使用 psycopg2-binary 替代',
            'mysqlclient': '建议使用预编译版本或 mysql-connector-python',
            'lxml': '建议使用预编译版本，如 lxml==4.9.2'
        }
        
        if package_name.lower() in compile_heavy_packages:
            return compile_heavy_packages[package_name.lower()]
        
        return "建议使用预编译包以避免编译问题"
    
    def install_with_fallback(self, package_name: str, version: str = None) -> Tuple[bool, str, str]:
        """
        带回退机制的安装方法
        :param package_name: 包名
        :param version: 版本
        :return: (成功, 输出, 错误)
        """
        # 安全处理版本参数
        if version is None or version.strip() == '':
            version = None
        
        # 首先尝试使用预编译包
        success, output, error = self.install_package(package_name, version)
        
        if not success and "Failed building wheel" in error:
            self._log_progress(f"预编译包安装失败，尝试使用源码安装: {package_name}", "warning")
            # 如果预编译包失败，尝试源码安装
            if version:
                install_cmd = [self.pip_path, 'install', f'{package_name}=={version}', '--no-cache-dir', '--no-binary=:all:', '-i', self.mirror_source]
            else:
                install_cmd = [self.pip_path, 'install', package_name, '--no-cache-dir', '--no-binary=:all:', '-i', self.mirror_source]
            
            try:
                result = subprocess.run(install_cmd, capture_output=True, text=True, timeout=300)
                if result.returncode == 0:
                    return True, result.stdout, ""
                else:
                    return False, result.stdout, result.stderr
            except subprocess.TimeoutExpired:
                return False, "", "安装超时"
            except Exception as e:
                return False, "", str(e)
        
        return success, output, error